ก้าวไปไกลกว่าพื้นฐานของ Flexbox เรียนรู้การจัดตำแหน่งและการกระจายขั้นสูงด้วย align-content, flex-grow, flex-shrink และสถานการณ์การจัดเลย์เอาต์ที่ใช้ได้จริง
เชี่ยวชาญ CSS Flexbox: การจัดตำแหน่งและการกระจายขั้นสูง
เป็นเวลาหลายปีที่ CSS Flexbox เป็นรากฐานสำคัญของเลย์เอาต์เว็บสมัยใหม่ นักพัฒนาส่วนใหญ่คุ้นเคยกับการใช้ display: flex เพื่อจัดเรียงไอเท็มในแถวหรือสร้างคอมโพเนนต์ที่จัดกึ่งกลางแบบง่ายๆ อย่างไรก็ตาม การจะเชี่ยวชาญ Flexbox ได้อย่างแท้จริงนั้นขึ้นอยู่กับความเข้าใจในคุณสมบัติที่ละเอียดอ่อนยิ่งขึ้นสำหรับการจัดตำแหน่งขั้นสูงและการกระจายแบบไดนามิก เมื่อคุณก้าวข้ามพื้นฐานของ justify-content: center และ align-items: center ไปแล้ว คุณจะปลดล็อกพลังในการสร้างเลย์เอาต์ที่ซับซ้อน ตอบสนองได้ดี และยืดหยุ่นโดยเนื้อแท้ได้อย่างง่ายดายอย่างน่าประหลาดใจ
คู่มือนี้สำหรับนักพัฒนาที่รู้พื้นฐานอยู่แล้วแต่ต้องการทำความเข้าใจให้ลึกซึ้งยิ่งขึ้น เราจะสำรวจคุณสมบัติที่ควบคุมการจัดตำแหน่งแบบหลายบรรทัด ตรรกะที่ซับซ้อนเบื้องหลังการขยายและหดตัวของไอเท็ม flex และรูปแบบที่ทรงพลังหลายอย่างที่ช่วยแก้ปัญหาความท้าทายในการจัดเลย์เอาต์ทั่วไป เตรียมพร้อมที่จะก้าวจากการเป็นผู้ใช้งานทั่วไปสู่สถาปนิก Flexbox ที่มีความมั่นใจ
พื้นฐานสำคัญ: ทบทวนสั้นๆ เกี่ยวกับแกนหลักและแกนตัดขวาง
ก่อนที่จะเจาะลึกหัวข้อขั้นสูง สิ่งสำคัญคือต้องมีความเข้าใจที่แม่นยำเกี่ยวกับแกนสองแกนที่ควบคุมทุกคอนเทนเนอร์ของ flex คุณสมบัติการจัดตำแหน่งและการกระจายทั้งหมดใน Flexbox ทำงานตามแกนใดแกนหนึ่งในสองแกนนี้
- แกนหลัก (The Main Axis): นี่คือแกนหลักที่ไอเท็ม flex ถูกจัดวางตามแนว ทิศทางของแกนนี้ถูกกำหนดโดยคุณสมบัติ
flex-direction - แกนตัดขวาง (The Cross Axis): แกนนี้จะตั้งฉากกับแกนหลักเสมอ
ประเด็นสำคัญคือแกนเหล่านี้ไม่ได้คงที่ พวกมันจะปรับทิศทางตามค่า flex-direction ของคุณ:
flex-direction: row(ค่าเริ่มต้น): แกนหลักเป็นแนวนอน (ซ้ายไปขวา) และแกนตัดขวางเป็นแนวตั้ง (บนลงล่าง)flex-direction: column: แกนหลักจะกลายเป็นแนวตั้ง (บนลงล่าง) และแกนตัดขวางจะกลายเป็นแนวนอน (ซ้ายไปขวา)flex-direction: row-reverse: แกนหลักเป็นแนวนอน แต่จะวิ่งจากขวาไปซ้ายflex-direction: column-reverse: แกนหลักเป็นแนวตั้ง แต่จะวิ่งจากล่างขึ้นบน
การลืมแนวคิดพื้นฐานนี้เป็นสาเหตุของความสับสนส่วนใหญ่เกี่ยวกับ Flexbox ควรถามตัวเองเสมอว่า: "แกนหลักของฉันชี้ไปทางไหน?" ก่อนที่จะใช้คุณสมบัติการจัดตำแหน่ง
เชี่ยวชาญการกระจายตามแกนหลักด้วย justify-content
คุณสมบัติ justify-content ควบคุมวิธีการกระจายช่องว่างระหว่างและรอบๆ ไอเท็ม flex ตาม แกนหลัก ในขณะที่ flex-start, flex-end และ center นั้นตรงไปตรงมา แต่พลังที่แท้จริงอยู่ที่ค่าที่ใช้ในการกระจายช่องว่าง
เจาะลึกการกระจายช่องว่าง
เรามาทำความเข้าใจความแตกต่างที่ละเอียดอ่อนแต่สำคัญยิ่งระหว่าง space-between, space-around และ space-evenly
-
justify-content: space-between;ค่านี้จะกระจายไอเท็มอย่างสม่ำเสมอตามแกนหลัก ไอเท็มแรกจะถูกดันไปที่จุดเริ่มต้นสุดของคอนเทนเนอร์ และไอเท็มสุดท้ายจะถูกดันไปที่จุดสิ้นสุดสุด พื้นที่ที่เหลือทั้งหมดจะถูกแบ่งเท่าๆ กัน ระหว่าง ไอเท็ม จะไม่มีช่องว่างที่ขอบด้านนอก
กรณีการใช้งาน: เหมาะสำหรับแถบนำทาง (navigation bars) ที่คุณต้องการให้โลโก้อยู่ทางซ้ายสุดและลิงก์อยู่ทางขวาสุด โดยมีระยะห่างที่เท่ากันระหว่างลิงก์
-
justify-content: space-around;ค่านี้จะกระจายไอเท็มโดยมีช่องว่างเท่ากัน รอบๆ แต่ละไอเท็ม ลองนึกภาพว่าแต่ละไอเท็มมี "ฟองอากาศ" ของช่องว่างทั้งด้านซ้ายและด้านขวา เมื่อฟองอากาศของไอเท็มที่อยู่ติดกันมาชนกัน ช่องว่างระหว่างไอเท็มจะดูเหมือนเป็นสองเท่าของช่องว่างที่ขอบของคอนเทนเนอร์ โดยเฉพาะอย่างยิ่ง ช่องว่างที่ขอบด้านนอกจะมีขนาดเป็นครึ่งหนึ่งของช่องว่างระหว่างไอเท็ม
กรณีการใช้งาน: มีประโยชน์สำหรับเลย์เอาต์แบบการ์ดหรือแกลเลอรีที่คุณต้องการให้ไอเท็มมีพื้นที่หายใจจากขอบของคอนเทนเนอร์ แต่ไม่ชิดขอบจนเกินไป
-
justify-content: space-evenly;นี่เป็นค่าที่เข้าใจง่ายที่สุดในสามค่านี้ มันทำให้แน่ใจว่าช่องว่างระหว่างไอเท็มสองชิ้นใดๆ จะเท่ากับช่องว่างระหว่างไอเท็มแรก/สุดท้ายกับขอบของคอนเทนเนอร์ทุกประการ ทุกช่องว่างจะเหมือนกันทั้งหมด
กรณีการใช้งาน: เหมาะอย่างยิ่งเมื่อคุณต้องการเลย์เอาต์ที่สมดุลและสมมาตรอย่างสมบูรณ์แบบ มักจะเป็นสิ่งที่นักออกแบบต้องการโดยนัยเมื่อพวกเขาขอ "ระยะห่างที่เท่ากัน"
พิชิตการจัดตำแหน่งตามแกนตัดขวางด้วย align-items และ align-self
ในขณะที่ justify-content จัดการแกนหลัก align-items จะจัดการการจัดตำแหน่งเริ่มต้นของไอเท็มตาม แกนตัดขวาง ภายในบรรทัดเดียว
ทำความเข้าใจค่าต่างๆ ของ `align-items`
align-items: stretch;(ค่าเริ่มต้น): นี่คือเหตุผลที่ไอเท็ม flex ของคุณมักจะดูเหมือนเติมเต็มความสูงของคอนเทนเนอร์โดยที่คุณไม่ได้สั่ง ไอเท็มจะยืดออกเพื่อเติมขนาดของคอนเทนเนอร์ตามแกนตัดขวาง (เช่น ความสูงในคอนเทนเนอร์ที่มีflex-direction: row)align-items: flex-start;: ไอเท็มจะถูกจัดชิดที่จุดเริ่มต้นของแกนตัดขวางalign-items: flex-end;: ไอเท็มจะถูกจัดชิดที่จุดสิ้นสุดของแกนตัดขวางalign-items: center;: ไอเท็มจะถูกจัดกึ่งกลางตามแนวแกนตัดขวางalign-items: baseline;: นี่เป็นค่าที่มีประสิทธิภาพและไม่ค่อยได้ใช้ ไอเท็มจะถูกจัดตำแหน่งเพื่อให้เส้นฐาน (baseline) ของข้อความเรียงตรงกัน สิ่งนี้มีประโยชน์อย่างยิ่งเมื่อคุณมีไอเท็มที่มีขนาดตัวอักษรต่างกัน (เช่น หัวข้อหลักข้างๆ หัวข้อรอง) และต้องการให้ข้อความจัดตำแหน่งตรงกัน ไม่ใช่แค่ตามขอบเขตของกล่อง
การลบล้างด้วย align-self
จะทำอย่างไรถ้าคุณต้องการให้ไอเท็มใดไอเท็มหนึ่งมีพฤติกรรมแตกต่างจากไอเท็มอื่น? นั่นคือหน้าที่ของ align-self ซึ่งใช้กับไอเท็ม flex แต่ละรายการ มันจะลบล้างคุณสมบัติ align-items ของคอนเทนเนอร์สำหรับไอเท็มนั้นๆ เพียงอย่างเดียว มันยอมรับค่าทั้งหมดเหมือนกับ align-items (บวกกับ `auto` ซึ่งจะรีเซ็ตกลับไปเป็นค่าของคอนเทนเนอร์)
ตัวอย่าง: ลองนึกภาพแถวของการ์ดที่จัดกึ่งกลางทั้งหมดด้วย align-items: center คุณสามารถทำให้การ์ด 'แนะนำ' ใบหนึ่งโดดเด่นขึ้นมาได้โดยใช้ align-self: stretch; กับการ์ดนั้น ทำให้มันสูงกว่าการ์ดอื่นๆ
พระเอกเบื้องหลัง: การกระจายขั้นสูงด้วย align-content
นี่อาจเป็นคุณสมบัติที่ถูกเข้าใจผิดมากที่สุดใน Flexbox และการเชี่ยวชาญมันคือเครื่องหมายของความสามารถขั้นสูง จุดที่มักสับสนคือความคล้ายคลึงกับ align-items
นี่คือกฎที่สำคัญที่สุด: align-content จะ ไม่มีผลใดๆ เมื่อไอเท็ม flex ของคุณทั้งหมดอยู่ในบรรทัดเดียว มันจะทำงานก็ต่อเมื่อคุณมีคอนเทนเนอร์ flex แบบหลายบรรทัด (เช่น คุณได้ตั้งค่า flex-wrap: wrap; และไอเท็มได้ตัดขึ้นบรรทัดใหม่แล้วจริงๆ)
ลองคิดแบบนี้:
align-itemsจัดตำแหน่งไอเท็ม ภายในบรรทัดของมันalign-contentจัดตำแหน่ง บรรทัดทั้งหมด ภายในคอนเทนเนอร์ มันควบคุมการกระจายช่องว่างในแกนตัดขวางระหว่างแถวของไอเท็ม
โดยพื้นฐานแล้วมันทำงานเหมือน justify-content แต่สำหรับแกนตัดขวาง ค่าของมันเกือบจะเหมือนกัน:
align-content: flex-start;(ค่าเริ่มต้น): ทุกบรรทัดจะถูกจัดชิดที่จุดเริ่มต้นของคอนเทนเนอร์align-content: flex-end;: ทุกบรรทัดจะถูกจัดชิดที่จุดสิ้นสุดalign-content: center;: ทุกบรรทัดจะถูกจัดชิดที่กึ่งกลางalign-content: space-between;: บรรทัดแรกจะอยู่ที่จุดเริ่มต้น บรรทัดสุดท้ายจะอยู่ที่จุดสิ้นสุด และช่องว่างจะถูกกระจายอย่างสม่ำเสมอระหว่างบรรทัดalign-content: space-around;: มีการวางช่องว่างเท่ากันรอบๆ แต่ละบรรทัดalign-content: space-evenly;: ระยะห่างระหว่างแต่ละบรรทัดจะเหมือนกันทั้งหมดalign-content: stretch;: บรรทัดต่างๆ จะยืดออกเพื่อใช้พื้นที่ที่เหลือ
กรณีการใช้งาน: ลองนึกภาพแกลเลอรีรูปภาพที่ไอเท็มต่างๆ มีการตัดขึ้นบรรทัดใหม่ หากคอนเทนเนอร์มีความสูงคงที่ อาจมีพื้นที่แนวตั้งเหลืออยู่ โดยค่าเริ่มต้น พื้นที่นี้จะปรากฏที่ด้านล่าง แต่ด้วยการใช้ align-content: space-between; หรือ align-content: center; คุณสามารถควบคุมการกระจายในแนวตั้งของตารางรูปภาพทั้งหมดของคุณ ทำให้ได้เลย์เอาต์ที่ดูเป็นมืออาชีพมากขึ้น
การปรับขนาดและการกระจายแบบไดนามิก: flex แบบย่อ
เลย์เอาต์แบบคงที่ไม่ค่อยพบเห็น พลังที่แท้จริงของ Flexbox มาจากความสามารถในการจัดการเนื้อหาและพื้นที่ว่างแบบไดนามิก สิ่งนี้ถูกควบคุมโดยสามคุณสมบัติ ซึ่งมักจะตั้งค่าผ่านรูปแบบย่อของ flex: flex-grow, flex-shrink และ flex-basis
1. flex-basis: จุดเริ่มต้น
ก่อนที่จะมีการขยายหรือหดตัวใดๆ เกิดขึ้น Flexbox ต้องการขนาดเริ่มต้นสำหรับแต่ละไอเท็ม นี่คือหน้าที่ของ flex-basis มันกำหนดขนาดเริ่มต้นขององค์ประกอบตามแกนหลัก
- หากตั้งค่าเป็นความยาวที่ระบุ (เช่น
200pxหรือ10rem) นั่นจะกลายเป็นขนาดเริ่มต้นของไอเท็ม - หากตั้งค่าเป็น
autoมันจะมองหาคุณสมบัติ `width` หรือ `height` บนไอเท็มนั้น หากไม่มี มันจะกำหนดขนาดตามเนื้อหาของไอเท็ม - หากตั้งค่าเป็น
0ไอเท็มจะไม่มีขนาดเริ่มต้น และขนาดสุดท้ายของมันจะถูกกำหนดโดยสัดส่วนflex-growของมันล้วนๆ
แนวทางปฏิบัติที่ดีที่สุด: มักจะดีกว่าถ้าใช้ flex-basis แทน `width` ในบริบทของ flex เนื่องจากเป็นการระบุขนาดของไอเท็มในบริบทของแกนหลักได้ชัดเจนกว่า
2. flex-grow: การใช้พื้นที่ส่วนเกิน
เมื่อคอนเทนเนอร์ flex มีพื้นที่ว่างเหลือตามแกนหลัก flex-grow จะเป็นตัวกำหนดว่าพื้นที่นั้นจะถูกกระจายอย่างไร มันเป็นค่าสัดส่วนที่ไม่มีหน่วย
- ค่าเริ่มต้นคือ
0ซึ่งหมายความว่าไอเท็มจะไม่ขยายเพื่อเติมเต็มพื้นที่ว่าง - หากทุกไอเท็มมี
flex-grow: 1พื้นที่ว่างจะถูกกระจายอย่างเท่าเทียมกันในหมู่ไอเท็มเหล่านั้น - หากไอเท็มหนึ่งมี
flex-grow: 2และอีกไอเท็มหนึ่งมีflex-grow: 1ไอเท็มแรกจะได้รับพื้นที่ว่างเป็นสองเท่าของไอเท็มที่สอง
3. flex-shrink: การจัดการพื้นที่ที่ไม่พอ (Overflow)
นี่คือส่วนที่ทำงานตรงข้ามกับ `flex-grow` เมื่อมีพื้นที่ในคอนเทนเนอร์ไม่เพียงพอที่จะใส่ไอเท็มทั้งหมดตาม `flex-basis` ของมัน ไอเท็มเหล่านั้นจะต้องหดตัว flex-shrink จะควบคุมว่าพวกมันจะหดตัวมากน้อยเพียงใด
- ค่าเริ่มต้นคือ
1ซึ่งหมายความว่าทุกไอเท็มจะหดตัวตามสัดส่วนโดยค่าเริ่มต้นเพื่อป้องกันการล้น - หากคุณตั้งค่า
flex-shrink: 0บนไอเท็มใด ไอเท็มนั้นจะไม่หดตัว มันจะรักษขนาด `flex-basis` ของมันไว้ ซึ่งอาจทำให้คอนเทนเนอร์ล้นได้ สิ่งนี้มีประโยชน์สำหรับองค์ประกอบเช่นโลโก้หรือปุ่มที่ไม่ควรถูกบีบอัด
รูปแบบย่อ flex: การรวมทุกอย่างเข้าด้วยกัน
คุณสมบัติ flex เป็นรูปแบบย่อสำหรับ flex-grow, flex-shrink และ flex-basis ตามลำดับ
flex: 0 1 auto;(ค่าเริ่มต้น): ไอเท็มไม่สามารถขยายได้, สามารถหดได้, และขนาดพื้นฐานของมันถูกกำหนดโดย width/height หรือเนื้อหาflex: 1;(รูปแบบย่อของflex: 1 1 0;): เป็นค่าที่ใช้บ่อยมาก ไอเท็มสามารถขยายและหดได้ และขนาดเริ่มต้นของมันคือ 0 สิ่งนี้ทำให้ไอเท็มแบ่งปันพื้นที่โดยอิงตามสัดส่วน flex-grow ของมันล้วนๆflex: auto;(รูปแบบย่อของflex: 1 1 auto;): ไอเท็มสามารถขยายและหดได้ และขนาดพื้นฐานของมันถูกกำหนดโดยเนื้อหาของมัน สิ่งนี้ช่วยให้ไอเท็มมีขนาดแตกต่างกันตามเนื้อหาของมัน แต่ยังคงสามารถดูดซับพื้นที่ว่างได้อย่างยืดหยุ่นflex: none;(รูปแบบย่อของflex: 0 0 auto;): ไอเท็มไม่มีความยืดหยุ่นเลย ไม่สามารถขยายหรือหดได้
กรณีการใช้งานจริงและสถานการณ์ขั้นสูง
สถานการณ์ที่ 1: Sticky Footer (เลย์เอาต์ Holy Grail)
ปัญหาคลาสสิกของการออกแบบเว็บ: จะทำอย่างไรให้ footer ติดอยู่ที่ด้านล่างของหน้า แม้ว่าเนื้อหาจะสั้น แต่ก็จะถูกดันลงไปตามธรรมชาติเมื่อเนื้อหายาว
.page-container {
display: flex;
flex-direction: column;
min-height: 100vh; /* Viewport Height */
}
.main-content {
flex-grow: 1; /* or flex: 1; */
}
ด้วยการทำให้คอนเทนเนอร์หลักของหน้าเป็น flexbox แบบคอลัมน์ และตั้งค่าพื้นที่เนื้อหาหลักเป็น flex-grow: 1 เรากำลังบอกให้มันใช้พื้นที่แนวตั้งที่มีอยู่ทั้งหมด ซึ่งจะผลัก footer ลงไปที่ด้านล่างของ viewport
สถานการณ์ที่ 2: ใช้ Auto Margins เพื่อแยกกลุ่ม
คุณจะสร้างแถบนำทางที่มีโลโก้อยู่ทางซ้ายสุดและกลุ่มของลิงก์อยู่ทางขวาสุดได้อย่างไร? ในขณะที่ justify-content: space-between ใช้งานได้ถ้าโลโก้เป็นไอเท็ม flex ชิ้นเดียว แต่ถ้าคุณมีหลายไอเท็มทางด้านขวาล่ะ?
ทางออกคือความมหัศจรรย์ของ auto margins ใน Flexbox
.navbar {
display: flex;
}
.logo {
/* No special properties needed */
}
.nav-links {
margin-left: auto;
}
ในคอนเทนเนอร์ flex, auto margin จะกินพื้นที่ว่างทั้งหมดในทิศทางที่กำหนดอย่างเต็มที่ ด้วยการตั้งค่า margin-left: auto บนกลุ่มของลิงก์นำทาง มันจะสร้างช่องว่างที่ยืดหยุ่นได้ระหว่างโลโก้และลิงก์ ผลักกลุ่มลิงก์ไปทางขวาสุด
สถานการณ์ที่ 3: Media Object
รูปแบบ UI ทั่วไปที่มีรูปภาพหรือไอคอนอยู่ด้านหนึ่งและข้อความอธิบายอยู่อีกด้านหนึ่ง ข้อความควรใช้พื้นที่ที่เหลือทั้งหมดและตัดคำอย่างสวยงาม
.media-object {
display: flex;
align-items: flex-start; /* Aligns image and text to the top */
}
.media-image {
margin-right: 1rem;
flex-shrink: 0; /* Prevents the image from being squished */
}
.media-body {
flex-grow: 1; /* Takes up all remaining horizontal space */
}
ในที่นี้ flex-grow: 1 บนคอนเทนเนอร์ข้อความคือกุญแจสำคัญ มันทำให้แน่ใจว่าไม่ว่ารูปภาพจะกว้างแค่ไหน ส่วนของข้อความก็จะขยายเพื่อเติมเต็มความกว้างที่เหลืออยู่ในคอนเทนเนอร์
สรุป: ก้าวข้ามการจัดตำแหน่ง สู่การวางเลย์เอาต์อย่างตั้งใจ
การเชี่ยวชาญ Flexbox หมายถึงการก้าวไปไกลกว่าแค่การจัดของให้อยู่กึ่งกลาง มันคือการทำความเข้าใจความสัมพันธ์ระหว่างแกนต่างๆ ตรรกะของการกระจายพื้นที่ และความยืดหยุ่นของการปรับขนาดไอเท็ม ด้วยการทำความเข้าใจอย่างถ่องแท้เกี่ยวกับ align-content สำหรับเลย์เอาต์หลายบรรทัด, รูปแบบย่อของ flex สำหรับการปรับขนาดแบบไดนามิก และรูปแบบที่ทรงพลังอย่าง auto margins คุณสามารถสร้างเลย์เอาต์ที่ไม่เพียงแต่สวยงาม แต่ยังแข็งแกร่ง ตอบสนองได้ดี และสะอาดในเชิงความหมาย (semantically clean)
ครั้งต่อไปที่คุณเผชิญกับความท้าทายในการจัดเลย์เอาต์ที่ซับซ้อน อย่าเพิ่งรีบใช้ floats หรือเทคนิคการจัดตำแหน่งที่ซับซ้อน แต่ให้ถามตัวเองว่า: ปัญหานี้สามารถแก้ไขได้ด้วยการกระจายพื้นที่อย่างตั้งใจหรือไม่? คำตอบส่วนใหญ่มักจะพบได้ในความสามารถขั้นสูงของ CSS Flexbox